package com.cosmoplat.xikai.business.service.impl;


import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cosmoplat.xikai.business.domain.AgvCallbackLog;
import com.cosmoplat.xikai.business.domain.BaseAlarmRecord;
import com.cosmoplat.xikai.business.domain.DeviceLog;
import com.cosmoplat.xikai.business.domain.DeviceThingModel;
import com.cosmoplat.xikai.business.domain.dto.agv.AgvCallbackRequest;
import com.cosmoplat.xikai.business.domain.dto.agv.ContinueTaskRequest;
import com.cosmoplat.xikai.business.domain.dto.agv.ContinueTaskResponse;
import com.cosmoplat.xikai.business.enums.DeviceParamsEnum;
import com.cosmoplat.xikai.business.mapper.AgvCallbackLogMapper;
import com.cosmoplat.xikai.business.mapper.BaseAlarmRecordMapper;
import com.cosmoplat.xikai.business.modbus.utils.Modbus4jUtils;
import com.cosmoplat.xikai.business.scada.ScadaData;
import com.cosmoplat.xikai.business.service.AgvService;
import com.cosmoplat.xikai.business.service.BaseAlarmRecordService;
import com.cosmoplat.xikai.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * @ClassName AgvServiceImpl
 * @Description AGV服务类 - 负责与AGV系统进行交互，包括调用继续执行任务接口
 * @Author wdf
 * @Date 2025/9/2
 */
@Slf4j
@Service
public class AgvServiceImpl extends ServiceImpl<AgvCallbackLogMapper, AgvCallbackLog> implements AgvService {

    /**
     * AGV服务的基础URL，从配置文件中读取
     */
    @Value("${agv.service-url.continueTask-url:http://localhost:8080/continueTask}")
    private String agvServiceBaseUrl;

    private final RestTemplate restTemplate;

    @Autowired
    private ScadaData scadaData;

    @Autowired
    private AgvCallbackLogMapper agvCallbackLogMapper;

    @Value("${device.code}")
    private String deviceCode;

    /**
     * 构造函数，初始化RestTemplate
     *
     * @param restTemplateBuilder RestTemplate构建器
     */
    public AgvServiceImpl(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder
                .setConnectTimeout(Duration.ofSeconds(5))  // 设置连接超时时间为5秒
                .setReadTimeout(Duration.ofSeconds(10))    // 设置读取超时时间为10秒
                .build();
    }

    /**
     *
     * 40048 点位为1的时候，设备暂停，允许agv小车进入，其他情况点位为0，不允许进入
     * 40246 轴封服务点位写1，告知PLC可以PLC小车已经离开，可以继续生产。
     * @param logRecord
     */
    @Override
    public void processApplyRequest(AgvCallbackLog logRecord) {
        try {
            // 调用PLC逻辑，查看 是否允许 agv 小车进入
            DeviceLog deviceLog = scadaData.getDeviceLog(DeviceParamsEnum.AGV_ALLOW_IN.getName());
            if (deviceLog == null || deviceLog.getVal() == null) {
                throw new ServiceException("与设备通讯异常，不能进入");
            }
            // 拒绝小车进入
            logRecord.setStatus(AgvCallbackLog.Status.REJECT);
            // 同意小车进入
            if (deviceLog.getVal().equals("1")) {
                logRecord.setStatus(AgvCallbackLog.Status.APPROVE);
            }
        } catch (Exception e) {
            logRecord.setStatus(AgvCallbackLog.Status.REJECT);
            log.error("agv处理异常:{}", e);
            throw e;
        } finally {
            logRecord.setUpdateTime(LocalDateTime.now());
            this.updateById(logRecord);
        }
    }

    /**
     *
     * 40246 轴封服务点位写1，告知PLC可以PLC小车已经离开，可以继续生产。
     * @param logRecord
     * @param request
     */
    @Override
    public void processReleaseRequest(AgvCallbackLog logRecord, AgvCallbackRequest request) {
        updateStatus(logRecord, AgvCallbackLog.Status.APPLY_OUT);
        // 调用PLC逻辑，agv 小车离开
        log.info("agv小车离开，修改PLC点位");

        DeviceThingModel deviceThingModel = scadaData.getDeviceThingModel(deviceCode, DeviceParamsEnum.AGV_ALLOW_OUT.getName(), 1);
        // 该点位设置为 46
        Modbus4jUtils.setRegisterValue(deviceThingModel.getSlaveId(), Integer.parseInt(deviceThingModel.getPoint()) + 200, Short.parseShort("1"));


        // 小车离开，设备逻辑调用调用成功
        updateStatus(logRecord, AgvCallbackLog.Status.FINISHED);
    }


    /**
     * 带重试机制的继续执行任务调用
     *
     * @param reqCode    请求编号
     * @param taskCode   任务单号
     * @param maxRetries 最大重试次数
     * @return 继续执行任务的响应结果
     */
    public ContinueTaskResponse continueTaskWithRetry(String reqCode, String taskCode, int maxRetries) {
        int retryCount = 0;
        while (retryCount <= maxRetries) {
            try {
                return continueTask(reqCode, taskCode);
            } catch (Exception e) {
                retryCount++;
                if (retryCount > maxRetries) {
                    log.error("AGV继续执行任务接口调用达到最大重试次数，reqCode: {}, taskCode: {}", reqCode, taskCode);
                    throw e;
                }

                log.warn("AGV继续执行任务接口调用失败，正在进行第{}次重试，reqCode: {}, taskCode: {}",
                        retryCount, reqCode, taskCode);

                // 等待一段时间后重试
                try {
                    Thread.sleep(1000 * retryCount); // 重试间隔逐渐增加
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("重试过程中被中断", ie);
                }
            }
        }

        // 理论上不会执行到这里
        throw new RuntimeException("AGV继续执行任务接口调用失败");
    }

    /**
     * 调用AGV继续执行任务接口
     * 当MES同意AGV进入工作区后，通过此接口通知AGV继续执行任务
     *
     * @param reqCode  请求编号
     * @param taskCode 任务单号
     * @return 继续执行任务的响应结果
     * @throws RuntimeException 当调用AGV接口失败时抛出异常
     */
    public ContinueTaskResponse continueTask(String reqCode, String taskCode) {
        log.info("调用AGV继续执行任务接口，reqCode: {}, taskCode: {}", reqCode, taskCode);

        // 构建请求参数
        ContinueTaskRequest request = new ContinueTaskRequest();
        request.setReqCode(reqCode);
        request.setTaskCode(taskCode);

        try {
            // 调用AGV继续执行接口
            ResponseEntity<ContinueTaskResponse> response = restTemplate.postForEntity(
                    agvServiceBaseUrl,
                    request,
                    ContinueTaskResponse.class
            );

            // 检查响应状态
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                ContinueTaskResponse responseBody = response.getBody();
                log.info("AGV继续执行任务接口调用成功，返回码: {}, 消息: {}",
                        responseBody.getCode(), responseBody.getMessage());
                return responseBody;
            } else {
                log.error("AGV继续执行任务接口调用失败，状态码: {}", response.getStatusCodeValue());
                throw new RuntimeException("AGV接口调用失败，状态码: " + response.getStatusCodeValue());
            }
        } catch (Exception e) {
            log.error("调用AGV继续执行任务接口时发生异常，reqCode: {}, taskCode: {}", reqCode, taskCode, e);
            throw new RuntimeException("调用AGV接口时发生异常: " + e.getMessage(), e);
        }
    }



    /**
     * 异步方式调度延迟执行 continueTask
     * 使用Spring的@Async注解实现异步执行
     *
     * @param logRecord
     * @param reqCode     请求编号
     * @param taskCode    任务单号
     * @param delayMillis 延迟时间（毫秒）
     */
    @Async
    public void asyncScheduleContinueTask(AgvCallbackLog logRecord, String reqCode, String taskCode, long delayMillis) {
        try {
            log.debug("异步调度 continueTask，将在 {} 毫秒后执行，reqCode: {}, taskCode: {}",
                    delayMillis, reqCode, taskCode);

            // 延迟执行
            Thread.sleep(delayMillis);

            log.debug("开始执行异步延迟的 continueTask，reqCode: {}, taskCode: {}", reqCode, taskCode);
//            ContinueTaskResponse response = agvService.continueTask(reqCode, taskCode);
            ContinueTaskResponse response = continueTaskWithRetry(reqCode, taskCode, 3);
            log.debug("异步延迟的 continueTask 执行完成，结果: {}", response);
            logRecord.setContinueTaskResult(JSONObject.toJSONString(response));
            updateStatus(logRecord, AgvCallbackLog.Status.CALL_SUCCESS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("异步调度 continueTask 被中断，reqCode: {}, taskCode: {}", reqCode, taskCode, e);
            logRecord.setContinueTaskResult(e.getMessage());
            updateStatus(logRecord, AgvCallbackLog.Status.CALL_FAIL);
        } catch (Exception e) {
            log.error("执行异步延迟的 continueTask 时发生异常，reqCode: {}, taskCode: {}",
                    reqCode, taskCode, e);
            logRecord.setContinueTaskResult(e.getMessage());
            updateStatus(logRecord, AgvCallbackLog.Status.CALL_FAIL);
        }
    }

    @Override
    public AgvCallbackLog getCallBackLog(AgvCallbackRequest request) {
        QueryWrapper<AgvCallbackLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("req_code", request.getReqCode());
        queryWrapper.eq("task_code", request.getTaskCode());
        List<AgvCallbackLog> list = this.list(queryWrapper);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        AgvCallbackLog agvCallbackLog = AgvCallbackLog.createLog(request.getReqCode(),
                request.getTaskCode(),
                AgvCallbackLog.Status.APPLY_IN,
                JSONObject.toJSONString(request),
                request.getMethod(),
                request.getRobotCode());
        this.save(agvCallbackLog);
        return agvCallbackLog;
    }

    @Override
    public List<AgvCallbackLog> waitingTaskList() {
        QueryWrapper<AgvCallbackLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .in(AgvCallbackLog::getStatus, AgvCallbackLog.Status.APPLY_IN, AgvCallbackLog.Status.REJECT);
        return list(queryWrapper);
    }

    @Override
    public void checkDeviceForContinueTask(List<AgvCallbackLog> waitingTaskList) {
        // 调用PLC逻辑，查看 是否允许 agv 小车进入
        DeviceLog deviceLog = scadaData.getDeviceLog(DeviceParamsEnum.AGV_ALLOW_IN.getName());
        if (deviceLog == null || deviceLog.getVal() == null) {
            log.error("与设备通讯异常，不能进入");
            return;
        }
        // 不同意小车进入
        if (!deviceLog.getVal().equals("1")) {
            log.info("agv小车等待中，当前设备状态不允许agv小车进入");
            return;
        }
        // 允许小车进入
        for (AgvCallbackLog logRecord : waitingTaskList) {
            asyncScheduleContinueTask(logRecord, logRecord.getReqCode(), logRecord.getTaskCode(), 1 * 1000);
        }
    }

    /**
     * 更新状态
     * @param logRecord
     * @param status
     */
    void updateStatus(AgvCallbackLog logRecord, Integer  status){
        logRecord.setStatus(status);
        logRecord.setUpdateTime(LocalDateTime.now());
        this.updateById(logRecord);
    }
}
